#[derive(Debug, Clone, PartialEq)]
pub enum Type {
    Int,
    Void,
    Array(ArrayType),
    Function(FunctionType),
}

#[derive(Debug, Clone, PartialEq)]
pub struct FunctionType {
    pub ret_ty: Box<Type>,
    pub params_type: Vec<Type>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct ArrayType {
    pub contained: Box<Type>,
    pub num_elements: usize,
}

pub struct TableManager {
    tables: Vec<Table>,
}

impl TableManager {
    pub fn new() -> Self {
        Self { tables: Vec::new() }
    }

    pub fn scope_push(&mut self) {
        self.tables.push(Table::new());
    }

    pub fn scope_pop(&mut self) {
        self.tables.pop();
    }

    pub fn sympol_push(&mut self, name: String, ty: Type) {
        self.tables.last_mut().unwrap().sympol_push(name, ty);
    }

    pub fn find(&self, name: &str) -> Option<Type> {
        // println!("finding {}", name);
        assert!(!self.tables.is_empty());
        // find nearlest
        for table in self.tables.iter().rev() {
            if let Some(ty) = table.find(name) {
                return Some(ty);
            }
        }
        None
    }

    // if //
    pub fn check(&self, name: &str) -> bool {
        self.tables.last().unwrap().check(name)
    }
}

struct Table {
    sympols: Vec<Sympol>,
}

impl Table {
    pub fn new() -> Self {
        Self {
            sympols: Vec::new(),
        }
    }

    pub fn sympol_push(&mut self, name: String, ty: Type) {
        self.sympols.push(Sympol { ty, name });
    }

    pub fn find(&self, name: &str) -> Option<Type> {
        for sym in &self.sympols {
            if name == sym.name {
                // println!("{} == {}", name, sym.name);
                return Some(sym.ty.clone());
            }
            // println!("{} != {}", name, sym.name);
        }
        None
    }

    pub fn check(&self, name: &str) -> bool {
        for sym in &self.sympols {
            if name == sym.name {
                return true;
            }
        }
        false
    }
}

pub struct Sympol {
    pub ty: Type,
    pub name: String,
}
